/**
 *	@file		delegate.h 
 *	@version	2.1a
 *	@author		rpvaras
 *
 *	This file is part of omcpp.
 *	
 *	
 *
 *	omcpp is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	omcpp is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with omcpp. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __delegate_h__
#define __delegate_h__ 1

namespace omcpp
{
	namespace _details
	{
		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		struct delegate_params_set
		{
			typedef delegate_params_set<_TyP1, _TyP2, _TyP3> TySelf;
			delegate_params_set( _TyP1 p1, _TyP2 p2, _TyP3 p3 )
				: param1(p1), param2(p2), param3(p3) {}
			delegate_params_set( const TySelf& copy )
				: param1(copy.param1), param2(copy.param2), param3(copy.param3) {}
			_TyP1 param1;
			_TyP2 param2;
			_TyP3 param3;
		};
		//--------------------------------------------------------
		template< typename _TyP1, typename _TyP2 >
		struct delegate_params_set<_TyP1, _TyP2, void>
		{
			typedef delegate_params_set<_TyP1, _TyP2> TySelf;
			delegate_params_set( _TyP1 p1, _TyP2 p2 )
				: param1(p1), param2(p2) {}
			delegate_params_set( const TySelf& copy )
				: param1(copy.param1), param2(copy.param2) {}
			_TyP1 param1;
			_TyP2 param2;
		};
		//--------------------------------------------------------
		template< typename _TyP1 >
		struct delegate_params_set<_TyP1, void, void>
		{
			typedef delegate_params_set<_TyP1> TySelf;
			delegate_params_set( _TyP1 p1 )
				: param1(p1) {}
			delegate_params_set( const TySelf& copy )
				: param1(copy.param1) {}
			_TyP1 param1;
		};
		//--------------------------------------------------------
		template<  >
		struct delegate_params_set<void, void, void>
		{
			typedef delegate_params_set<> TySelf;
			delegate_params_set( ) {}
			delegate_params_set( const TySelf& copy ) {}
		};

		//--------------------------------------------------------
		//--------------------------------------------------------
		// Forward declaration
		template< typename _TyRet, typename _TyP1, typename _TyP2, typename _TyP3 >
		class abstract_delegate;
		//--------------------------------------------------------
		// Forward declaration
		template< typename _TyThis, typename _TyMethod, typename _TyRet, typename _TyP1, typename _TyP2, typename _TyP3>
		class method_delegate;

		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		class delegate_traits
		{
		public:
			typedef _TyRet TyRet;
			typedef _TyP1 TyP1;
			typedef _TyP2 TyP2;
			typedef _TyP3 TyP3;
			typedef delegate_params_set<_TyP1, _TyP2, _TyP3> TyParamsSet;
			typedef abstract_delegate<_TyRet, _TyP1, _TyP2, _TyP3> TyAbsDelegate;
		};
		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyThis, typename _TyMethod, typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		class method_delegate_traits : public delegate_traits<_TyRet, _TyP1, _TyP2, _TyP3>
		{
		public:
			typedef _TyThis TyThis;
			typedef _TyMethod TyMethod;
			typedef method_delegate<_TyThis, _TyMethod, _TyRet, _TyP1, _TyP2, _TyP3> TyDelegate;
		};
		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyFunction, typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		class function_delegate_traits : public delegate_traits<_TyRet, _TyP1, _TyP2, _TyP3>
		{
		public:
			typedef _TyFunction TyFunction;
		};

		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		class abstract_delegate
		{
		public:
			typedef delegate_traits<_TyRet, _TyP1, _TyP2, _TyP3> Traits;
		public:
			virtual inline _TyRet invoke(_TyP1, _TyP2, _TyP3) = 0;
			virtual typename Traits::TyAbsDelegate* clone() = 0;
			virtual void release() = 0;
		};
		//--------------------------------------------------------
		template< typename _TyRet, typename _TyP1, typename _TyP2 >
		class abstract_delegate<_TyRet, _TyP1, _TyP2, void>
		{
		public:
			typedef delegate_traits<_TyRet, _TyP1, _TyP2> Traits;
		public:
			virtual inline _TyRet invoke(_TyP1, _TyP2) = 0;
			virtual typename Traits::TyAbsDelegate* clone() = 0;
			virtual void release() = 0;
		};
		//--------------------------------------------------------
		template< typename _TyRet, typename _TyP1 >
		class abstract_delegate<_TyRet, _TyP1, void, void>
		{
		public:
			typedef delegate_traits<_TyRet, _TyP1> Traits;
		public:
			virtual inline _TyRet invoke(_TyP1) = 0;
			virtual typename Traits::TyAbsDelegate* clone() = 0;
			virtual void release() = 0;
		};
		//--------------------------------------------------------
		template< typename _TyRet >
		class abstract_delegate<_TyRet, void, void, void>
		{
		public:
			typedef delegate_traits<_TyRet> Traits;
		public:
			virtual inline _TyRet invoke() = 0;
			virtual typename Traits::TyAbsDelegate* clone() = 0;
			virtual void release() = 0;
		};

		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyThis, typename _TyMethod, typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		class method_delegate_base
			: public abstract_delegate<_TyRet, _TyP1, _TyP2, _TyP3> 
		{
		public:
			typedef method_delegate_traits<_TyThis, _TyMethod, _TyRet, _TyP1, _TyP2, _TyP3> Traits;

		public:
			method_delegate_base()
				: _lpThis(0), _lpMethod(0) {}

			void bind(_TyThis* lpThis, _TyMethod lpMethod)
			{
				_lpThis = lpThis;
				_lpMethod = lpMethod;
			}
			virtual typename Traits::TyAbsDelegate* clone()
			{
				Traits::TyDelegate* cloned = new Traits::TyDelegate;
				cloned->_lpThis = _lpThis;
				cloned->_lpMethod = _lpMethod;
				return cloned;
			}

		protected:
			_TyThis* _lpThis;
			_TyMethod _lpMethod;
		};

		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyThis, typename _TyMethod, typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		class method_delegate
			: public method_delegate_base<_TyThis, _TyMethod, _TyRet, _TyP1, _TyP2, _TyP3>
		{
		public:
			virtual inline _TyRet invoke(_TyP1 p1, _TyP2 p2, _TyP3 p3)
			{
				return (_lpThis->*_lpMethod)(p1, p2, p3);
			}
			virtual void release()
			{
				delete this;
			}
		};
		//--------------------------------------------------------
		template< typename _TyThis, typename _TyMethod, typename _TyRet, typename _TyP1, typename _TyP2>
		class method_delegate<_TyThis, _TyMethod, _TyRet, _TyP1, _TyP2, void>
			: public method_delegate_base<_TyThis, _TyMethod, _TyRet, _TyP1, _TyP2>
		{
		public:
			virtual inline _TyRet invoke(_TyP1 p1, _TyP2 p2)
			{
				return (_lpThis->*_lpMethod)(p1, p2);
			}
			virtual void release()
			{
				delete this;
			}
		};
		//--------------------------------------------------------
		template< typename _TyThis, typename _TyMethod, typename _TyRet, typename _TyP1>
		class method_delegate<_TyThis, _TyMethod, _TyRet, _TyP1, void, void>
			: public method_delegate_base<_TyThis, _TyMethod, _TyRet, _TyP1>
		{
		public:
			virtual inline _TyRet invoke(_TyP1 p1)
			{
				return (_lpThis->*_lpMethod)(p1);
			}
			virtual void release()
			{
				delete this;
			}
		};
		//--------------------------------------------------------
		template< typename _TyThis, typename _TyMethod, typename _TyRet >
		class method_delegate<_TyThis, _TyMethod, _TyRet, void, void, void>
			: public method_delegate_base<_TyThis, _TyMethod, _TyRet>
		{
		public:
			virtual inline _TyRet invoke()
			{
				return (_lpThis->*_lpMethod)();
			}
			virtual void release()
			{
				delete this;
			}
		};

		//--------------------------------------------------------
		//--------------------------------------------------------
		template< typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
		class ex_delegate_base
		{
		public:
			typedef ex_delegate_base<_TyRet, _TyP1, _TyP2, _TyP3> TySelf;
			typedef delegate_traits<_TyRet, _TyP1, _TyP2, _TyP3> Traits;

		protected:
			ex_delegate_base()
			{
				_lpAbsDelegate = 0;
				_lpParamsSet = 0;
			}
		public:
			virtual ~ex_delegate_base()
			{
				_unbind();
				_unbind_params();
			}

		protected:
			void copy_from(const TySelf& copy)
			{
				_unbind();
				_unbind_params();
				if(0 != copy._lpAbsDelegate)
					_lpAbsDelegate = copy._lpAbsDelegate->clone();
				if(0 != copy._lpParamsSet)
					_lpParamsSet = new Traits::TyParamsSet(*(copy._lpParamsSet));
			}

			void _bind(typename Traits::TyAbsDelegate* lpAbsDelegate)
			{
				_unbind();
				_lpAbsDelegate = lpAbsDelegate;
			}

			void _unbind()
			{
				if(0 != _lpAbsDelegate)
				{
					_lpAbsDelegate->release();
					_lpAbsDelegate = 0;
				}
			}

			void _bind_params(typename Traits::TyParamsSet* lpParamsSet)
			{
				_unbind_params();
				_lpParamsSet = lpParamsSet;
			}

			void _unbind_params()
			{
				if(0 != _lpParamsSet)
				{
					delete _lpParamsSet;
					_lpParamsSet = 0;
				}
			}

		protected:
			typename Traits::TyAbsDelegate* _lpAbsDelegate;
			typename Traits::TyParamsSet* _lpParamsSet;
		};
	};

	//--------------------------------------------------------
	//--------------------------------------------------------
	template< typename _TyRet, typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
	class ex_delegate
		: public omcpp::_details::ex_delegate_base<_TyRet, _TyP1, _TyP2, _TyP3>
	{
	public:
		typedef ex_delegate<_TyRet, _TyP1, _TyP2, _TyP3> TySelf;
		typedef ex_delegate_base<_TyRet, _TyP1, _TyP2, _TyP3> TyBase;

	public:
		ex_delegate() {}
		ex_delegate(const TySelf& copy)
		{
			TyBase::copy_from(static_cast<const TyBase&>(copy));
		}
		template< typename _TyThis, typename _TyX >
		TySelf& bind( _TyThis* lpThis, _TyRet(_TyX::*lpMethod)(_TyP1, _TyP2, _TyP3) )
		{
			typedef _TyRet(_TyX::*_TyMethod)(_TyP1, _TyP2, _TyP3);
			typedef _details::method_delegate_traits<_TyThis, _TyMethod, _TyRet, _TyP1, _TyP2, _TyP3> Traits;
			Traits::TyDelegate* lpDelegate = new Traits::TyDelegate;
			lpDelegate->bind(lpThis, lpMethod);
			this->_bind(lpDelegate);
			return *this;
		}
		void bind_params(_TyP1 p1, _TyP2 p2, _TyP3 p3)
		{
			_bind_params( new Traits::TyParamsSet(p1, p2, p3) );
		}
		inline _TyRet invoke(_TyP1 p1, _TyP2 p2, _TyP3 p3)
		{
			return _lpAbsDelegate->invoke(p1, p2, p3);
		}
		inline _TyRet invoke()
		{
			return invoke(_lpParamsSet->param1, _lpParamsSet->param2, _lpParamsSet->param3);
		}

	public:
		inline _TyRet operator()(_TyP1 p1, _TyP2 p2, _TyP3 p3)
		{
			return invoke(p1, p2, p3);
		}
	};
	//--------------------------------------------------------
	template< typename _TyRet, typename _TyP1, typename _TyP2 >
	class ex_delegate<_TyRet, _TyP1, _TyP2, void>
		: public omcpp::_details::ex_delegate_base<_TyRet, _TyP1, _TyP2>
	{
	public:
		typedef ex_delegate<_TyRet, _TyP1, _TyP2> TySelf;
		typedef ex_delegate_base<_TyRet, _TyP1, _TyP2> TyBase;

	public:
		ex_delegate() {}
		ex_delegate(const TySelf& copy)
		{
			TyBase::copy_from(static_cast<const TyBase&>(copy));
		}
		template< typename _TyThis, typename _TyX >
		void bind( _TyThis* lpThis, _TyRet(_TyX::*lpMethod)(_TyP1, _TyP2) )
		{
			typedef _TyRet(_TyX::*_TyMethod)(_TyP1, _TyP2);
			typedef _details::method_delegate_traits<_TyThis, _TyMethod, _TyRet, _TyP1, _TyP2> Traits;
			Traits::TyDelegate* lpDelegate = new Traits::TyDelegate;
			lpDelegate->bind(lpThis, lpMethod);
			this->_bind(lpDelegate);
		}
		void bind_params(_TyP1 p1, _TyP2 p2)
		{
			_bind_params( new Traits::TyParamsSet(p1, p2) );
		}
		inline _TyRet invoke(_TyP1 p1, _TyP2 p2)
		{
			return _lpAbsDelegate->invoke(p1, p2);
		}
		inline _TyRet invoke()
		{
			return invoke(_lpParamsSet->param1, _lpParamsSet->param2);
		}

	public:
		inline _TyRet operator()(_TyP1 p1, _TyP2 p2)
		{
			return invoke(p1, p2);
		}
	};
	//--------------------------------------------------------
	template< typename _TyRet, typename _TyP1 >
	class ex_delegate<_TyRet, _TyP1, void, void>
		: public omcpp::_details::ex_delegate_base<_TyRet, _TyP1>
	{
	public:
		typedef ex_delegate<_TyRet, _TyP1> TySelf;
		typedef ex_delegate_base<_TyRet, _TyP1> TyBase;

	public:
		ex_delegate() {}
		ex_delegate(const TySelf& copy)
		{
			TyBase::copy_from(static_cast<const TyBase&>(copy));
		}
		template< typename _TyThis, typename _TyX >
		void bind( _TyThis* lpThis, _TyRet(_TyX::*lpMethod)(_TyP1) )
		{
			typedef _TyRet(_TyX::*_TyMethod)(_TyP1);
			typedef _details::method_delegate_traits<_TyThis, _TyMethod, _TyRet, _TyP1> Traits;
			Traits::TyDelegate* lpDelegate = new Traits::TyDelegate;
			lpDelegate->bind(lpThis, lpMethod);
			this->_bind(lpDelegate);
		}
		void bind_params(_TyP1 p1)
		{
			_bind_params( new Traits::TyParamsSet(p1) );
		}
		inline _TyRet invoke(_TyP1 p1)
		{
			return _lpAbsDelegate->invoke(p1);
		}
		inline _TyRet invoke()
		{
			return invoke(_lpParamsSet->param1);
		}

	public:
		inline _TyRet operator()(_TyP1 p1)
		{
			return invoke(p1);
		}
	};
	//--------------------------------------------------------
	template< typename _TyRet >
	class ex_delegate<_TyRet, void, void, void>
		: public omcpp::_details::ex_delegate_base<_TyRet>
	{
	public:
		typedef ex_delegate<_TyRet> TySelf;
		typedef ex_delegate_base<_TyRet> TyBase;

	public:
		ex_delegate() {}
		ex_delegate(const TySelf& copy)
		{
			TyBase::copy_from(static_cast<const TyBase&>(copy));
		}
		template< typename _TyThis, typename _TyX >
		void bind( _TyThis* lpThis, _TyRet(_TyX::*lpMethod)() )
		{
			typedef _TyRet(_TyX::*_TyMethod)();
			typedef _details::method_delegate_traits<_TyThis, _TyMethod, _TyRet> Traits;
			Traits::TyDelegate* lpDelegate = new Traits::TyDelegate;
			lpDelegate->bind(lpThis, lpMethod);
			this->_bind(lpDelegate);
		}
		inline _TyRet invoke()
		{
			return _lpAbsDelegate->invoke();
		}

	public:
		inline _TyRet operator()()
		{
			return invoke();
		}
	};

	//--------------------------------------------------------
	//--------------------------------------------------------
	template< typename _TyP1 = void, typename _TyP2 = void, typename _TyP3 = void >
	class delegate : public ex_delegate< void, _TyP1, _TyP2, _TyP3 >
	{
	};
	//--------------------------------------------------------
	template< typename _TyP1, typename _TyP2 >
	class delegate<_TyP1, _TyP2, void> : public ex_delegate< void, _TyP1, _TyP2 >
	{
	};
	//--------------------------------------------------------
	template< typename _TyP1 >
	class delegate<_TyP1, void, void> : public ex_delegate< void, _TyP1 >
	{
	};
	//--------------------------------------------------------
	template< >
	class delegate<void, void, void> : public ex_delegate< void >
	{
	};
};

#endif //__delegate_h__